#pragma once

#include <unistd.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <pthread.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include "LOG_CYC.hpp"


class ShmService
{
public:
    static const bool SHM_CREATE = 1;  // 创建共享内存
    static const bool SHM_CONNECT = 0; // 连接共享内存

    static const char* SHM_PATH;

public:
    // POSIX 共享内存实现
    class ShmServicePOSIX
    {
    public:
        // 获取shm组件
        static ShmServicePOSIX* GetInstance(
            size_t size,      // 创建共享内存的大小
            bool mode,        // 创建/连接 这块共享内存
            const std::string name) // 共享内存对象名称
        {
            return new ShmServicePOSIX(size, mode, name);
        }

        // 获取共享内存起始地址
        void*& GetShmHeader() {
            return _shmHeader;
        }

        size_t GetShmSize() {
            return _shmSize;
        }

        // 去除当前进程关联的共享内存
        void detach() {
            shm_detach();
        }

        ~ShmServicePOSIX() {
            detach();

            // 关闭共享内存
            if (_isMaster)
                shm_delete();
        }

    private:
        void InitServer() {
            // 1. 创建/连接 共享内存
            if (_isMaster)
                shm_create(); // 创建
            else
                shm_connect(); // 连接

            // 2. 挂载到当前进程虚拟内存中
            shm_mount();
        }

        // 创建一块共享内存
        void shm_create() {
            // 打开共享内存对象，如果存在则先删除
            shm_unlink(_shmName.c_str());

            //LOG_CYC(NORMAL, "创建共享内存文件: %s", _shmName.c_str());

            _shmFd = shm_open(_shmName.c_str(), O_CREAT | O_EXCL | O_RDWR, 0666);
            if (_shmFd < 0) {
                LOG_CYC(ERROR, "创建共享内存失败，fd: %d，错误信息: %s", _shmFd, strerror(errno));
                exit(1);
            }
            if (ftruncate(_shmFd, _shmSize) == -1) {
                LOG_CYC(ERROR, "设置共享内存大小失败，大小: %ld，错误信息: %s", _shmSize, strerror(errno));
                exit(2);
            }
            //LOG_CYC(NORMAL, "创建共享内存成功，fd: %d", _shmFd);
        }

        // 与一块共享内存建立连接
        void shm_connect() {
            _shmFd = shm_open(_shmName.c_str(), O_RDWR, 0666);
            if (_shmFd < 0) {
                LOG_CYC(ERROR, "打开共享内存文件失败：%s", _shmName.c_str());
                exit(3);
            }
            LOG_CYC(NORMAL, "成功打开共享内存文件：%s, 在当前进程fileId: %d", _shmName.c_str(), _shmFd);
        }

        // 挂载到当前进程虚拟内存中
        void shm_mount() {
            _shmHeader = mmap(NULL, _shmSize, PROT_READ | PROT_WRITE, MAP_SHARED, _shmFd, 0);
            if (_shmHeader == MAP_FAILED) {
                LOG_CYC(ERROR, "mmap failed, fd : %d", _shmFd);
                exit(4);
            }
            LOG_CYC(NORMAL, "mmap done, addr : %p，_shmFd : %d", _shmHeader, _shmFd);
        }

        // 去除当前关联的共享内存
        void shm_detach() {
            // 如果共享内存起始地址为空，直接返回
            if (_shmHeader == nullptr)
                return;

            // 取消映射共享内存
            if (munmap(_shmHeader, _shmSize) == -1) {
                LOG_CYC(FATAL, "detach shm failed", nullptr);
                exit(5);
            }
            LOG_CYC(NORMAL, "去除当前关联的共享内存，fileName：%s", _shmName.c_str());
        }

        // 关闭共享内存
        void shm_delete() {
            // 如果文件描述符小于0，表示共享内存未打开，直接返回
            if (_shmFd < 0)
                return;

            // 关闭共享内存文件描述符
            if (close(_shmFd) == -1) {
                LOG_CYC(FATAL, "close shm fd failed", nullptr);
                exit(7);
            }

            // 尝试解除共享内存的链接，如果失败，记录致命错误日志并退出程序
            if (shm_unlink(_shmName.c_str()) == -1) {
                LOG_CYC(FATAL, "delete shm failed, error: %s, name: %s", strerror(errno), _shmName.c_str());
                exit(6);
            }

            // 记录正常删除共享内存的日志
            LOG_CYC(NORMAL, "已删除共享内存文件, fileName：%s", _shmName.c_str());
        }

        ShmServicePOSIX(size_t size, bool mode, const std::string name)
            : _shmSize(size), _isMaster(mode), _shmName(name) {
            InitServer();
        }

        ShmServicePOSIX(const ShmServicePOSIX&) = delete;

    private:
        size_t _shmSize;            // 共享内存大小
        bool _isMaster;             // 是否为创建共享内存的进程
        std::string _shmName;       // 共享内存名称
        int _shmFd = -1;            // 共享内存文件描述符
        void* _shmHeader = nullptr; // 共享内存起始地址
    };


    /*
        system V 的生命周期是跟随内存的，如果启动后，异常终止，没有在程序中释放，需要手动在系统中释放
        ipcs -m 查看当前存在的共享内存段
        ipcrm -m [shmid] 关闭id为shmid的共享内存段
    */

    // System V 共享内存实现
    class ShmServiceSystemV
    {
    public:
        // 获取shm组件
        static ShmServiceSystemV* GetInstance(
            size_t size,          // 创建共享内存的大小
            bool mode,            // 创建/连接 这块共享内存
            int ProjID,           // 路径下key的ID
            const char* PathName) // 存放 共享内存标识key的路径

        {
            return new ShmServiceSystemV(size, mode, ProjID, PathName);
        }

        // 获取共享内存起始地址
        char* GetShmHeader() {
            return _shmHeader;
        }

        size_t GetShmSize() {
            return _shmSize;
        }

        void detach() {
            // 去除当前进程关联的共享内存
            shm_detach();
        }

        ~ShmServiceSystemV() {
            detach();

            // 关闭共享内存
            if (_isMaster)
                shm_delete();
        }

    private:
        void InitServer() {
            // 1.创建key
            shm_key();

            // 2.创建/连接 共享内存
            if (_isMaster)
                shm_create(); // 创建
            else
                shm_connect(); // 连接

            // 3.挂载到当前进程虚拟内存中
            shm_mount();
        }

        // 创建识别共享内存位置的key
        void shm_key() {
            _shmkey = ftok(_shmPathName.c_str(), _shmProjID);
            if (_shmkey < 0) {
                LOG_CYC(ERROR, "create key failed, 0x%x", _shmkey);
                exit(1);
            }
            LOG_CYC(NORMAL, "create key done, 0x%x", _shmkey);
        }

        // 创建一块共享内存
        void shm_create() {
            _shmid = shmget(_shmkey, _shmSize, IPC_CREAT | IPC_EXCL | 0666);
            if (_shmid < 0) {
                LOG_CYC(ERROR, "get shm failed, shmid : %d", _shmid);
                exit(2);
            }
            LOG_CYC(NORMAL, "get shm done, shmid : %d", _shmid);
        }

        // 与一块共享内存建立连接
        void shm_connect() {
            _shmid = shmget(_shmkey, _shmSize, 0);
            if (_shmid < 0) {
                LOG_CYC(ERROR, "get shm failed, 0x%x", _shmkey);
                exit(3);
            }
            LOG_CYC(NORMAL, "get shm done, 0x%x", _shmkey);
        }

        // 挂载到当前进程虚拟内存中
        void shm_mount() {
            _shmHeader = (char*)shmat(_shmid, nullptr, 0);
            if (_shmHeader == nullptr) {
                LOG_CYC(ERROR, "create shmaddr failed, shmid : %d", _shmkey);
                exit(4);
            }
            LOG_CYC(NORMAL, "create shmaddr done, shmaddr : 0x%x", (void*)_shmHeader);
        }

        // 去除当前关联的共享内存
        void shm_detach() {
            if (_shmHeader)
                return;

            if (shmdt(_shmHeader) < 0) {
                LOG_CYC(FATAL, "detach shm failed", nullptr);
                exit(5);
            }
            LOG_CYC(NORMAL, "detach shm done", nullptr);
        }

        // 关闭共享内存
        void shm_delete() {
            if (_shmid < 0)
                return;

            if (shmctl(_shmid, IPC_RMID, nullptr) < 0) {
                LOG_CYC(FATAL, "delete shm failed", nullptr);
                exit(6);
            }
            LOG_CYC(NORMAL, "delete shm done, shmid : %d", _shmid);
        }

        ShmServiceSystemV(size_t size, bool mode, int ProjID, const std::string PathName)
            : _shmSize(size), _isMaster(mode), _shmPathName(PathName), _shmProjID(ProjID) {
            InitServer();
        }

        ShmServiceSystemV(const ShmServiceSystemV&) = delete;

    private:
        size_t _shmSize = -1;     // 共享内存大小
        bool _isMaster = true;    // 是否为创建共享内存的进程
        const std::string _shmPathName; // key路径
        int _shmProjID;           // 与key关联的子序列

        key_t _shmkey = -1;         // 共享内存的key
        int _shmid = -1;            // 共享内存标识符
        char* _shmHeader = nullptr; // 共享内存起始地址
    };
};

const char* ShmService::SHM_PATH = "/SC";